#include "PLImageFactory.h"

#include "PLBlob.h"
#include "PLConversionMethods.h"
#include "PLDirectoryMethods.h"
#include "PLGlobals.h"
#include "PLOpenCVExtensionMethods.h"
#include "PLMosaic.h"
#include "PLVertex.h"

#include <iostream>
#include <map>
#include <set>
#include <string>
#include <vector>

#include "opencv2\opencv.hpp"

using cv::fillConvexPoly;
using cv::interpolatedLine;
using cv::line;
using cv::Mat;
using cv::Point;
using cv::Scalar;
using cv::Vec3d;

using namespace PL;

using std::cout;
using std::endl;
using std::map;
using std::min;
using std::set;
using std::string;
using std::vector;

const PLuint U = 0U;
const PLuint V = 1U;

const PLuint R = 2U;
const PLuint G = 1U;
const PLuint B = 0U;

/**
  *	Constructs a new PLImageFactory object
  *
  * @param mosaic const pointer to the PLMosaic object the images are generated for
  */
PLImageFactory::PLImageFactory( const PLMosaic *mosaic )
{
	mMosaic = mosaic;

	if (!mosaic)
		mIsNull = true;
}

/**
  *	Generates the levelled textures which have the same name as the original
  * images with a suffix "_levelled" and PNG as the image file format
  *
  * @param path path of the directory, that is used to store the generated images into it
  * @param imageFormat enum value corresponding to the image format of the generated images
  * @return true when the generation was successful, false otherwise
  */
bool PLImageFactory::GenerateLevelledTextures( const std::string &path, const PLImageFormat &imageFormat )
{
	if (mIsNull)
		return false;
		
	// OpenCV data structures BEGIN
	const PLImageInfo *image_info = 0;

	IplImage *img = 0;
	IplImage *img_lab = 0;
	Mat image_matrix;
	Mat levelling_matrix;
	Mat current_null_matrix;
	Mat result;
	//Mat result_lab;

	Point begin;
	Point end;

	Point begin_backup;
	Point end_backup;

	// COLOR FOR TESTING
// 	Scalar color;
// 	double value = 30.0;
// 	color[0] = value;
// 	color[1] = value;
// 	color[2] = value;

	const PLuint NUM_POINTS_PER_POLYGON = 3U;

	Point polygon[NUM_POINTS_PER_POLYGON];

	int num_polygons = 0;
	int current_point_index = 0;

	Scalar begin_color;
	Scalar end_color;
	// OpenCV data structures END

	Scalar vertex_colors[NUM_POINTS_PER_POLYGON];
	Scalar sampled_color;

 	int current_width = 0;
	int current_height = 0;

	int sampling_x = 0;
	int sampling_y = 0;

	const vector<double*> *texture_coordinates = &mMosaic->GetTextureCoordinates();
	const PLuint index_offset = mMosaic->GetIndexOffset();
	PLuint texcoord_index = 0;
	PLuint base_texcoord_index = 0;

	const PLuint channels[] = { R, G, B };
	const char* channel_names[] = { "Red", "Green", "Blue" };

	const double *base_vertex_color_bgr = 0;

	PLVec2d vertex_normal;
	bool first_triangle_of_ring_segment = true;

	string image_name;

	// For every material-image
// 	for (map<string, set<PLBlob*>>::const_iterator image_it = mMosaic->GetImageNamesToBlobsMap().begin();
// 		image_it != mMosaic->GetImageNamesToBlobsMap().end(); image_it++)
	for (map<string, set<PLBlob*>>::const_iterator image_it = mMosaic->GetMaterialNamesToBlobsMap().begin();
		image_it != mMosaic->GetMaterialNamesToBlobsMap().end(); image_it++)
	{
		image_name = mMosaic->GetMaterialNamesToImageNamesMap().find( image_it->first )->second;

		img = cvLoadImage( (mMosaic->GetBasePath() + PL_FILE_SEPARATOR + image_name).c_str() );
		//img_lab = cvCreateImage( cvSize( img->width, img->height), img->depth, img->nChannels );

		// TEST
		//cvCvtColor( img, img_lab, CV_BGR2Lab );

		image_matrix = Mat(img);
		//image_matrix = Mat( img_lab );

		image_info = &mMosaic->GetImageNamesToImageInfosMap().find( image_name )->second;

		if (image_info->width != current_width || image_info->height != current_height)
		{
			levelling_matrix = Mat::zeros( image_info->height, image_info->width, CV_64FC3 );
			current_null_matrix = Mat::zeros( image_info->height, image_info->width, CV_64FC3 );
		} else
		{
			current_null_matrix.assignTo( levelling_matrix );
		}

		// For every blob inside the image
		for (set<PLBlob*>::const_iterator blob_it = image_it->second.begin();
			blob_it != image_it->second.end(); blob_it++)
		{
			// For every triangle inside the blob
			for (std::vector<PLTriangle*>::const_iterator triangle_it = (*blob_it)->GetTriangleVector().begin();
				triangle_it != (*blob_it)->GetTriangleVector().end(); triangle_it++)
			{
				current_point_index = 0;

				// For every edge inside the Triangle
				for (std::vector<PLEdge*>::const_iterator edge_it = (*triangle_it)->GetEdges().begin();
					edge_it != (*triangle_it)->GetEdges().end(); edge_it++)
				{
					texcoord_index = (*edge_it)->From().GetTexcoordIndex() - index_offset;
					polygon[current_point_index].x = static_cast<int>((*texture_coordinates)[texcoord_index][U] * image_info->width);
					polygon[current_point_index].y = static_cast<int>((1.0 - (*texture_coordinates)[texcoord_index][V]) * image_info->height);

					memcpy( &vertex_colors[current_point_index][B], (*edge_it)->From().GetLevellingBGR(), 3 * sizeof(double) );

					current_point_index++;
				}

				fillConvexPolyInterpolated( levelling_matrix, polygon, NUM_POINTS_PER_POLYGON, vertex_colors );
			}

			first_triangle_of_ring_segment = true;

			// For every triangle inside the outline triangle ring
			for (vector<PLTriangle*>::const_iterator triangle_it = (*blob_it)->GetOutlineTriangleRingVector().begin();
				triangle_it != (*blob_it)->GetOutlineTriangleRingVector().end(); triangle_it++)
			{
				current_point_index = 0;

				// For every edge of the triangle
				for (vector<PLEdge*>::const_iterator edge_it = (*triangle_it)->GetEdges().begin();
					edge_it != (*triangle_it)->GetEdges().end(); edge_it++)
				{
					texcoord_index = (*edge_it)->From().GetTexcoordIndex() - index_offset;
					polygon[current_point_index].x = static_cast<int>((*texture_coordinates)[texcoord_index][U] * image_info->width);
					polygon[current_point_index].y = static_cast<int>((1.0 - (*texture_coordinates)[texcoord_index][V]) * image_info->height);

 					if (0 == current_point_index)
					{
						// Set the normal and color of the base vertex
						if (!first_triangle_of_ring_segment)
						{
							base_texcoord_index = (*edge_it)->From().GetTexcoordIndex() - index_offset;
							base_vertex_color_bgr = (*edge_it)->From().GetLevellingBGR();
						}

						memcpy( &vertex_colors[current_point_index][B], (*edge_it)->From().GetLevellingBGR(), 3 * sizeof(double) );
					}
					else if (1 == current_point_index)
					{
						// Point is one of the outliners without color info
						// -> sample it

						// Remember: cv::Mat::at( int y, int x )

						// Is it the first triangle in the ring segment?
						if (first_triangle_of_ring_segment)
						{
							base_texcoord_index = (*edge_it)->To().GetTexcoordIndex() - index_offset;
							base_vertex_color_bgr = (*edge_it)->To().GetLevellingBGR();
						}

						vertex_normal = PLVec2d( (*texture_coordinates)[base_texcoord_index][U] - (*texture_coordinates)[texcoord_index][U],
							(*texture_coordinates)[base_texcoord_index][V] - (*texture_coordinates)[texcoord_index][V] );

						sampling_x = static_cast<int>(((*texture_coordinates)[texcoord_index][U] + (2 * vertex_normal[U])) * image_info->width);
						sampling_y = static_cast<int>((1.0 - ((*texture_coordinates)[texcoord_index][V] + (2 * vertex_normal[V]))) * image_info->height);


						if (sampling_x > -1 && sampling_x < image_info->width && sampling_y > -1 && sampling_y < image_info->height)
							memcpy( &sampled_color[B],
								&levelling_matrix.at<Vec3d>( sampling_y, sampling_x )[0], 3 * sizeof(double) );
						else
							memcpy( &sampled_color[B], base_vertex_color_bgr, 3 * sizeof(double) );
						
						memcpy( &vertex_colors[current_point_index][B], base_vertex_color_bgr, 3 * sizeof(double) );

						vertex_colors[current_point_index] += (vertex_colors[current_point_index] - sampled_color);

						(*edge_it)->From().SetLevellingColorBGR( &vertex_colors[current_point_index][B] );
					}
					else
					{
						memcpy( &vertex_colors[current_point_index][B], (*edge_it)->From().GetLevellingBGR(), 3 * sizeof(double) );
					}

					current_point_index++;
				}

				fillConvexPolyInterpolated( levelling_matrix, polygon, NUM_POINTS_PER_POLYGON, vertex_colors );

				first_triangle_of_ring_segment = !first_triangle_of_ring_segment;
			}
		}

		// Bring the [0 1] values back to the [0 255] range
		levelling_matrix *= 255.0;

		image_matrix.convertTo( image_matrix, levelling_matrix.type() );

		result = image_matrix + levelling_matrix;
		

		// IplImage iplimg = mat;

		//result.convertTo( result, CV_8UC3 );

		//result_lab = Mat( result.rows, result.cols, result.type() );

		//cvCvtColor( &IplImage(result), &IplImage(result_lab), CV_Lab2BGR );

		vector<int> jpeg_options;
		jpeg_options.push_back( CV_IMWRITE_JPEG_QUALITY );
		jpeg_options.push_back( 100 );

		if(imageFormat == PL_IMAGEFORMAT_JPEG)
		{
			cv::imwrite( (path + PL_FILE_SEPARATOR +
				RemoveFileExtension( image_name ) + "_" +
				mMosaic->GetMaterialNamesToLevelledTextureSuffixesMap().find( image_it->first )->second + PL_LEVELLED_FILE_NAME_SUFFIX + PL_IMAGEFORMAT_FILE_EXTENSIONS[imageFormat]).c_str(),
				result,
				jpeg_options );
		}
		else
		{
			cv::imwrite( (path + PL_FILE_SEPARATOR +
				RemoveFileExtension( image_name ) + "_" +
				mMosaic->GetMaterialNamesToLevelledTextureSuffixesMap().find( image_it->first )->second + PL_LEVELLED_FILE_NAME_SUFFIX + PL_IMAGEFORMAT_FILE_EXTENSIONS[imageFormat]).c_str(),
				result );
		}

		// release the image
		cvReleaseImage( &img );
		//cvReleaseImage( &img_lab );
	}

	return true;
}

/**
  *	Generates the levelling function textures which have the same name as the original
  * images with a suffix "_levellingFunction" and PNG as the image file format.
  * Notice that values <0 in the levelling function are always 0 in the generated
  * image.
  *
  * @param path path of the directory, that is used to store the generated images into it
  * @param imageFormat enum value corresponding to the image format of the generated images
  * @return true when the generation was successful, false otherwise
  */
bool PLImageFactory::GenerateLevellingFunctionTextures( const std::string &path, const PLImageFormat &imageFormat )
{
	if (mIsNull)
		return false;

	// OpenCV data structures BEGIN
	const PLImageInfo *image_info = 0;

	//IplImage *img = 0;
	//Mat image_matrix;
	Mat levelling_matrix;
	Mat current_null_matrix;
	//Mat result;

	Point begin;
	Point end;

	// COLOR FOR TESTING
	Scalar color;
	double value = 30.0;
	color[0] = value;
	color[1] = value;
	color[2] = value;

	const PLuint NUM_POINTS_PER_POLYGON = 3U;

	Point polygon[NUM_POINTS_PER_POLYGON];

	int num_polygons = 0;
	int current_point_index = 0;

	Scalar begin_color;
	Scalar end_color;
	// OpenCV data structures END

	Scalar vertex_colors[NUM_POINTS_PER_POLYGON];

	int current_width = 0;
	int current_height = 0;

	const vector<double*> *texture_coordinates = &mMosaic->GetTextureCoordinates();
	PLuint index_offset = mMosaic->GetIndexOffset();
	PLuint texcoord_index = 0;

	string image_name;

	// For every material-image
	// 	for (map<string, set<PLBlob*>>::const_iterator image_it = mMosaic->GetImageNamesToBlobsMap().begin();
	// 		image_it != mMosaic->GetImageNamesToBlobsMap().end(); image_it++)
	for (map<string, set<PLBlob*>>::const_iterator image_it = mMosaic->GetMaterialNamesToBlobsMap().begin();
		image_it != mMosaic->GetMaterialNamesToBlobsMap().end(); image_it++)
	{
		image_name = mMosaic->GetMaterialNamesToImageNamesMap().find( image_it->first )->second;

		image_info = &mMosaic->GetImageNamesToImageInfosMap().find( image_name )->second;

		if (image_info->width != current_width || image_info->height != current_height)
		{
			levelling_matrix = Mat::zeros( image_info->height, image_info->width, CV_64FC3 );
			current_null_matrix = Mat::zeros( image_info->height, image_info->width, CV_64FC3 );
		} else
		{
			current_null_matrix.assignTo( levelling_matrix );
		}

		// For every blob inside the image
		for (set<PLBlob*>::const_iterator blob_it = image_it->second.begin();
			blob_it != image_it->second.end(); blob_it++)
		{
			// For every triangle inside the blob
			for (std::vector<PLTriangle*>::const_iterator triangle_it = (*blob_it)->GetTriangleVector().begin();
				triangle_it != (*blob_it)->GetTriangleVector().end(); triangle_it++)
			{
				current_point_index = 0;

				// For every edge inside the Triangle
				for (std::vector<PLEdge*>::const_iterator edge_it = (*triangle_it)->GetEdges().begin();
					edge_it != (*triangle_it)->GetEdges().end(); edge_it++)
				{
					texcoord_index = (*edge_it)->From().GetTexcoordIndex() - index_offset;
					polygon[current_point_index].x = min( image_info->width - 1, static_cast<int>((*texture_coordinates)[texcoord_index][U] * image_info->width) );
					polygon[current_point_index].y = min( image_info->height - 1, static_cast<int>((1.0 - (*texture_coordinates)[texcoord_index][V]) * image_info->height) );

					memcpy( &vertex_colors[current_point_index][B], (*edge_it)->From().GetLevellingBGR(), 3 * sizeof(double) );

					current_point_index++;
				}

				fillConvexPolyInterpolated( levelling_matrix, polygon, NUM_POINTS_PER_POLYGON, vertex_colors );
			}
		}

		// Bring the [0 1] values back to the [0 255] range
		levelling_matrix *= 255.0;

		vector<int> jpeg_options;
		jpeg_options.push_back( CV_IMWRITE_JPEG_QUALITY );
		jpeg_options.push_back( 100 );

		if(imageFormat == PL_IMAGEFORMAT_JPEG)
		{
			cv::imwrite( (path + PL_FILE_SEPARATOR +
				RemoveFileExtension( image_name ) + "_" +
				mMosaic->GetMaterialNamesToLevelledTextureSuffixesMap().find( image_it->first )->second + "_levelingFunction" + PL_IMAGEFORMAT_FILE_EXTENSIONS[imageFormat]).c_str(),
				levelling_matrix,
				jpeg_options );
		}
		else
		{
			cv::imwrite( (path + PL_FILE_SEPARATOR +
				RemoveFileExtension( image_name ) + "_" +
				mMosaic->GetMaterialNamesToLevelledTextureSuffixesMap().find( image_it->first )->second + "_levelingFunction" + PL_IMAGEFORMAT_FILE_EXTENSIONS[imageFormat]).c_str(),
				levelling_matrix );
		}

		// Old CV-Function, replaced by cv::imwrite
// 		cvSaveImage( (path + PL_FILE_SEPARATOR +
// 			RemoveFileExtension( image_name ) + "_" +
// 			mMosaic->GetMaterialNamesToLevelledTextureSuffixesMap().find( image_it->first )->second + "_levelingFunction" + PL_IMAGEFORMAT_FILE_EXTENSIONS[imageFormat]).c_str(),
// 			&IplImage( levelling_matrix ) );
	}

	return true;
}

/**
  *	Generates the leveling function value textures which have the same name as the original
  * images with a suffix "_levellingFunctionValues_" followed by the channel name ("Red", "Green" or "Blue")
  * and PNG as the image file format.
  *
  * @param path path of the directory, that is used to store the generated images into it
  * @param channel PLChannel enum value corresponding to the desired RGB channel
  * @param imageFormat enum value corresponding to the image format of the generated images
  * @return true when the generation was successful, false otherwise
  */
bool PLImageFactory::GenerateLevellingFunctionValueTextures( const std::string &path, const PLChannel &channel, const PLImageFormat &imageFormat )
{
	if (mIsNull)
		return false;

	const PLImageInfo *image_info = 0;

	Mat current_matrix;
	Mat current_null_matrix;

	Point point;

	int current_width = 0;
	int current_height = 0;

	const vector<double*> *texture_coordinates = &mMosaic->GetTextureCoordinates();
	const PLuint index_offset = mMosaic->GetIndexOffset();
	PLuint texcoord_index = 0;

	// White color for the lines
	Scalar color( 255.0, 255.0, 255.0 );

	// Find the points that have to be drawn
	set<PLVertex*> image_points;

	double levelling_value = 0.0;
	string suffix;

	switch (channel)
	{
	case PL_CHANNEL_RED:
		suffix = "Red";
		break;
	case PL_CHANNEL_GREEN:
		suffix = "Green";
		break;
	case PL_CHANNEL_BLUE:
		suffix = "Blue";
		break;
	}

	string image_name;

	// For every material-image
	// 	for (map<string, set<PLBlob*>>::const_iterator image_it = mMosaic->GetImageNamesToBlobsMap().begin();
	// 		image_it != mMosaic->GetImageNamesToBlobsMap().end(); image_it++)
	for (map<string, set<PLBlob*>>::const_iterator image_it = mMosaic->GetMaterialNamesToBlobsMap().begin();
		image_it != mMosaic->GetMaterialNamesToBlobsMap().end(); image_it++)
	{
		image_name = mMosaic->GetMaterialNamesToImageNamesMap().find( image_it->first )->second;

		image_info = &mMosaic->GetImageNamesToImageInfosMap().find( image_name )->second;

		if (image_info->width != current_width && image_info->height != current_height)
		{
			current_matrix = Mat::zeros( image_info->height, image_info->width, CV_8U );
			current_null_matrix = Mat::zeros( image_info->height, image_info->width, CV_8U );
		} else
		{
			current_null_matrix.assignTo( current_matrix );
		}

		image_points.clear();

		// For every blob inside the image
		for (set<PLBlob*>::const_iterator blob_it = image_it->second.begin();
			blob_it != image_it->second.end(); blob_it++)
		{
			// For every triangle inside the blob
			for (std::vector<PLTriangle*>::const_iterator triangle_it = (*blob_it)->GetTriangleVector().begin();
				triangle_it != (*blob_it)->GetTriangleVector().end(); triangle_it++)
			{
				// For every edge inside the Triangle
				for (std::vector<PLEdge*>::const_iterator edge_it = (*triangle_it)->GetEdges().begin();
					edge_it != (*triangle_it)->GetEdges().end(); edge_it++)
				{
					image_points.insert( &(*edge_it)->From() );
				}
			}
		}

		// For every point inside the image
		for(set<PLVertex*>::const_iterator point_it = image_points.begin();
			point_it != image_points.end(); point_it++)
		{
			texcoord_index = (*point_it)->GetTexcoordIndex() - index_offset;
			point.x = min( image_info->width - 1, static_cast<int>((*texture_coordinates)[texcoord_index][U] * image_info->width) );
			point.y = min( image_info->height - 1, static_cast<int>((1.0 - (*texture_coordinates)[texcoord_index][V]) * image_info->height) );

			line( current_matrix, point, point, color );

			switch (channel)
			{
			case PL_CHANNEL_RED:
				levelling_value = (*point_it)->GetLevellingR();
				break;
			case PL_CHANNEL_GREEN:
				levelling_value = (*point_it)->GetLevellingG();
				break;
			case PL_CHANNEL_BLUE:
				levelling_value = (*point_it)->GetLevellingB();
				break;
			}

			putText( current_matrix, DoubleToString( floor( levelling_value * 255.0 + 0.5 ) ), point + Point( -10, -5 ),
				cv::FONT_HERSHEY_SIMPLEX, 1, color );
		}

		cvSaveImage( (path + PL_FILE_SEPARATOR +
			RemoveFileExtension( image_name ) + "_levelingFunctionValues_" + suffix + PL_IMAGEFORMAT_FILE_EXTENSIONS[imageFormat]).c_str(),
			&current_matrix.operator IplImage() );
	}

	return true;
}

/**
  *	Generates the area masks
  *
  * @param path path of the directory, that is used to store the generated images into it
  * @param imageFormat enum value corresponding to the image format of the generated images
  * @return true when the generation was successful, false otherwise
  */
bool PLImageFactory::GenerateAreaMasks( const std::string &path, const PLImageFormat &imageFormat )
{
	if (mIsNull)
		return false;

	// OpenCV data structures BEGIN
	const PLImageInfo *image_info = 0;

	Mat levelling_matrix;
	Mat current_null_matrix;

	Point begin;
	Point end;

	const PLuint NUM_POINTS_PER_POLYGON = 3U;

	Point polygon[NUM_POINTS_PER_POLYGON];

	int num_polygons = 0;
	int current_point_index = 0;

	// OpenCV data structures END

	Scalar color_white( 255.0, 255.0, 255.0 );

	int current_width = 0;
	int current_height = 0;

	const vector<double*> *texture_coordinates = &mMosaic->GetTextureCoordinates();
	PLuint index_offset = mMosaic->GetIndexOffset();
	PLuint texcoord_index = 0;

	string image_name;

	for (map<string, set<PLBlob*>>::const_iterator image_it = mMosaic->GetMaterialNamesToBlobsMap().begin();
		image_it != mMosaic->GetMaterialNamesToBlobsMap().end(); image_it++)
	{
		image_name = mMosaic->GetMaterialNamesToImageNamesMap().find( image_it->first )->second;

		image_info = &mMosaic->GetImageNamesToImageInfosMap().find( image_name )->second;

		if (image_info->width != current_width || image_info->height != current_height)
		{
			levelling_matrix = Mat::zeros( image_info->height, image_info->width, CV_64FC3 );
			current_null_matrix = Mat::zeros( image_info->height, image_info->width, CV_64FC3 );
		} else
		{
			current_null_matrix.assignTo( levelling_matrix );
		}

		// For every blob inside the image
		for (set<PLBlob*>::const_iterator blob_it = image_it->second.begin();
			blob_it != image_it->second.end(); blob_it++)
		{
			// For every triangle inside the blob
			for (std::vector<PLTriangle*>::const_iterator triangle_it = (*blob_it)->GetTriangleVector().begin();
				triangle_it != (*blob_it)->GetTriangleVector().end(); triangle_it++)
			{
				current_point_index = 0;

				// For every edge inside the Triangle
				for (std::vector<PLEdge*>::const_iterator edge_it = (*triangle_it)->GetEdges().begin();
					edge_it != (*triangle_it)->GetEdges().end(); edge_it++)
				{
					texcoord_index = (*edge_it)->From().GetTexcoordIndex() - index_offset;
					polygon[current_point_index].x = min( image_info->width - 1, static_cast<int>((*texture_coordinates)[texcoord_index][U] * image_info->width) );
					polygon[current_point_index].y = min( image_info->height - 1, static_cast<int>((1.0 - (*texture_coordinates)[texcoord_index][V]) * image_info->height) );

					current_point_index++;
				}
				
				cv::fillConvexPoly( levelling_matrix, polygon, NUM_POINTS_PER_POLYGON, color_white );
			}
		}

		cvSaveImage( (path + PL_FILE_SEPARATOR +
			RemoveFileExtension( image_name ) + "_" +
			mMosaic->GetMaterialNamesToLevelledTextureSuffixesMap().find( image_it->first )->second + "_areaMask" + PL_IMAGEFORMAT_FILE_EXTENSIONS[imageFormat]).c_str(),
			&IplImage( levelling_matrix ) );
	}

	return true;
}

/**
  *	Generates the mesh structure masks which have the same name as the original
  * images with a suffix "_meshStructureMask" and PNG as the image file format
  *
  * @param path path of the directory, that is used to store the generated images into it
  * @param withOutlineTriangleRing true when the outline triangle ring should also be rendered into the generated images, false otherwise
  * @param imageFormat enum value corresponding to the image format of the generated images
  * @return true when the generation was successful, false otherwise
  */
bool PLImageFactory::GenerateMeshStructureMasks( const std::string &path, bool withOutlineTriangleRing, const PLImageFormat &imageFormat )
{
	if (mIsNull)
		return false;

	const PLImageInfo *image_info = 0;

	Mat current_matrix;
	Mat current_null_matrix;

	Point begin;
	Point end;

	int current_width = 0;
	int current_height = 0;

	const vector<double*> *texture_coordinates = &mMosaic->GetTextureCoordinates();
	PLuint index_offset = mMosaic->GetIndexOffset();
	PLuint texcoord_index = 0;

	// White color for the lines
	Scalar color( 255.0, 255.0, 255.0 );

	// Find the edges that have to be drawn
	set<PLEdge*, PLEdge::PLEdgeVIAndTIPCompare> image_edges;

	string image_name;

	// For every material-image
	// 	for (map<string, set<PLBlob*>>::const_iterator image_it = mMosaic->GetImageNamesToBlobsMap().begin();
	// 		image_it != mMosaic->GetImageNamesToBlobsMap().end(); image_it++)
	for (map<string, set<PLBlob*>>::const_iterator image_it = mMosaic->GetMaterialNamesToBlobsMap().begin();
		image_it != mMosaic->GetMaterialNamesToBlobsMap().end(); image_it++)
	{
		image_name = mMosaic->GetMaterialNamesToImageNamesMap().find( image_it->first )->second;

		image_info = &mMosaic->GetImageNamesToImageInfosMap().find( image_name )->second;

		if (image_info->width != current_width && image_info->height != current_height)
		{
			current_matrix = Mat::zeros( image_info->height, image_info->width, CV_8U );
			current_null_matrix = Mat::zeros( image_info->height, image_info->width, CV_8U );
		} else
		{
			current_null_matrix.assignTo( current_matrix );
		}

		image_edges.clear();

		// For every blob inside the image
		for (set<PLBlob*>::const_iterator blob_it = image_it->second.begin();
			blob_it != image_it->second.end(); blob_it++)
		{
			// For every triangle inside the blob
			for (std::vector<PLTriangle*>::const_iterator triangle_it = (*blob_it)->GetTriangleVector().begin();
				triangle_it != (*blob_it)->GetTriangleVector().end(); triangle_it++)
			{
				// For every edge inside the Triangle
				for (std::vector<PLEdge*>::const_iterator edge_it = (*triangle_it)->GetEdges().begin();
					edge_it != (*triangle_it)->GetEdges().end(); edge_it++)
				{
					image_edges.insert( (*edge_it) );
				}
			}

			if (withOutlineTriangleRing)
			{
				// For every triangle inside the outline triangle ring
				for (vector<PLTriangle*>::const_iterator triangle_it = (*blob_it)->GetOutlineTriangleRingVector().begin();
					triangle_it != (*blob_it)->GetOutlineTriangleRingVector().end(); triangle_it++)
				{
					// For every edge of the triangle
					for (vector<PLEdge*>::const_iterator edge_it = (*triangle_it)->GetEdges().begin();
						edge_it != (*triangle_it)->GetEdges().end(); edge_it++)
					{
						image_edges.insert( (*edge_it) );
					}
				}
			}
		}

		// For every edge inside the image
		for(set<PLEdge*, PLEdge::PLEdgeVIAndTIPCompare>::const_iterator edge_it = image_edges.begin();
			edge_it != image_edges.end(); edge_it++)
		{
			texcoord_index = (*edge_it)->From().GetTexcoordIndex() - index_offset;
			begin.x = min( image_info->width - 1, static_cast<int>((*texture_coordinates)[texcoord_index][U] * image_info->width) );
			begin.y = min( image_info->height - 1, static_cast<int>((1.0 - (*texture_coordinates)[texcoord_index][V]) * image_info->height) );

			texcoord_index = (*edge_it)->To().GetTexcoordIndex() - index_offset;
			end.x = min( image_info->width - 1, static_cast<int>((*texture_coordinates)[texcoord_index][U] * image_info->width) );
			end.y = min( image_info->height - 1, static_cast<int>((1.0 - (*texture_coordinates)[texcoord_index][V]) * image_info->height) );
			
			line( current_matrix, begin, end, color );
		}

		cvSaveImage( (path + PL_FILE_SEPARATOR +
			RemoveFileExtension( image_name ) + "_" +
			mMosaic->GetMaterialNamesToLevelledTextureSuffixesMap().find( image_it->first )->second +
			"_meshStructureMask" + PL_IMAGEFORMAT_FILE_EXTENSIONS[imageFormat]).c_str(),
			&current_matrix.operator IplImage() );
	}

	return true;
}

/**
  *	Generates the outline masks which have the same name as the original
  * images with a suffix "_outlineMask" and PNG as the image file format
  *
  * @param path path of the directory, that is used to store the generated images into it
  * @param imageFormat enum value corresponding to the image format of the generated images
  * @return true when the generation was successful, false otherwise
  */
bool PLImageFactory::GenerateOutlineMasks( const std::string &path, const PLImageFormat &imageFormat )
{
	if (mIsNull)
		return false;

	const PLImageInfo *image_info = 0;

	Mat current_matrix;
	Mat current_null_matrix;

	Point begin;
	Point end;

	int current_width = 0;
	int current_height = 0;

	const vector<double*> *texture_coordinates = &mMosaic->GetTextureCoordinates();
	PLuint index_offset = mMosaic->GetIndexOffset();
	PLuint texcoord_index = 0;

	// White color for the lines
	Scalar color( 255.0, 255.0, 255.0 );

	string image_name;

	// For every material-image
	// 	for (map<string, set<PLBlob*>>::const_iterator image_it = mMosaic->GetImageNamesToBlobsMap().begin();
	// 		image_it != mMosaic->GetImageNamesToBlobsMap().end(); image_it++)
	for (map<string, set<PLBlob*>>::const_iterator image_it = mMosaic->GetMaterialNamesToBlobsMap().begin();
		image_it != mMosaic->GetMaterialNamesToBlobsMap().end(); image_it++)
	{
		image_name = mMosaic->GetMaterialNamesToImageNamesMap().find( image_it->first )->second;

		image_info = &mMosaic->GetImageNamesToImageInfosMap().find( image_name )->second;

		if (image_info->width != current_width && image_info->height != current_height)
		{
			current_matrix = Mat::zeros( image_info->height, image_info->width, CV_64FC3 );//CV_8U );
			current_null_matrix = Mat::zeros( image_info->height, image_info->width, CV_64FC3 );//CV_8U );
		} else
		{
			current_null_matrix.assignTo( current_matrix );
		}

		// For every blob inside the image
		for (set<PLBlob*>::const_iterator blob_it = image_it->second.begin();
			blob_it != image_it->second.end(); blob_it++)
		{
			// For every outline edge inside the blob
			for(set<PLEdge*, PLEdge::PLEdgeVIPCompare>::const_iterator edge_it = (*blob_it)->GetOutlineEdgeSet().begin();
				edge_it != (*blob_it)->GetOutlineEdgeSet().end(); edge_it++)
			{
				texcoord_index = (*edge_it)->From().GetTexcoordIndex() - index_offset;
				begin.x = static_cast<int>((*texture_coordinates)[texcoord_index][U] * image_info->width);
				begin.y = static_cast<int>((1.0 - (*texture_coordinates)[texcoord_index][V]) * image_info->height);

				texcoord_index = (*edge_it)->To().GetTexcoordIndex() - index_offset;
				end.x = static_cast<int>((*texture_coordinates)[texcoord_index][U] * image_info->width);
				end.y = static_cast<int>((1.0 - (*texture_coordinates)[texcoord_index][V]) * image_info->height);

				line( current_matrix, begin, end, color );

				// Draw the normals

// 				double from_ring_texcoord[2];
// 				PLuint from_texcoord_index = (*edge_it)->From().GetTexcoordIndex() - index_offset;
// 
// 				// Copy the existing texture coordinates to be the base of the triangle ring
// 				memcpy( from_ring_texcoord, (*texture_coordinates)[from_texcoord_index], 2 * sizeof(double) );
// 
// 				double widthInImageSpace = static_cast<double>(50.0) / image_info->width;
// 				double IMAGE_RATIO = static_cast<double>(image_info->width) / image_info->height;
// 
// 				// Add the normals to define the width of the triangle ring
// 				from_ring_texcoord[U] += widthInImageSpace * (*((*edge_it)->From().GetNormal( (*blob_it)->GetID() )))[U];
// 				from_ring_texcoord[V] += IMAGE_RATIO * widthInImageSpace * (*((*edge_it)->From().GetNormal( (*blob_it)->GetID() )))[V];
// 
// 				end.x = static_cast<int>( from_ring_texcoord[U] * image_info->width );
// 				end.y = static_cast<int>( (1.0 - from_ring_texcoord[V]) * image_info->height );
// 
// 				line( current_matrix, begin, end, color );
			}
		}

		cvSaveImage( (path + PL_FILE_SEPARATOR +
			RemoveFileExtension( image_name ) + "_" +
			mMosaic->GetMaterialNamesToLevelledTextureSuffixesMap().find( image_it->first )->second + "_outlineMask" +
			PL_IMAGEFORMAT_FILE_EXTENSIONS[imageFormat]).c_str(),
			&current_matrix.operator IplImage() );
	}

	return true;
}